package hku.cs.lunuo.rank;

import hku.cs.lunuo.Global;

import java.io.File;
import java.util.Iterator;
import java.util.List;

import soot.Body;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Unit;
import soot.options.Options;
import soot.tagkit.LineNumberTag;
import soot.toolkits.graph.Block;
import soot.toolkits.graph.BlockGraph;
import soot.toolkits.graph.ExceptionalBlockGraph;
import soot.util.Chain;

public abstract class BlkBasingModel implements Model {

	BlkStatistic blkStatistic;
	StmtStatistic stmtStatistic = new StmtStatistic();
	StmtResult stmtResult = new StmtResult();
	
	@Override
	public void analyze() {
		doAnalyze(blkStatistic);
		blkStatistic.toSortedXML(new File(Global.v().toDoDir, Global.v().modelName+"blk.xml"));
	}
	
	protected abstract void doAnalyze(BlkStatistic blkStatistic);
	
	@Override
	public void toStmt() {
		Scene.v().loadBasicClasses();
		Options.v().set_keep_line_number(true);
		Options.v().setPhaseOption("jb", "use-original-names:true");
		
		for (String className : blkStatistic.getKlassNames()) {
			SootClass sClass = Scene.v().loadClassAndSupport(className);
			for (String methodName : blkStatistic.getMethodNames(className)) {
				
				SootMethod m = sClass.getMethod(methodName);
				if (m.isAbstract())
					continue;
				Body b = m.retrieveActiveBody();
				// it will error when the class is abstract
				BlockGraph bGraph = new ExceptionalBlockGraph(b);
				Chain<Unit> units = b.getUnits();
		
				for(Integer idx : blkStatistic.getBlks(className, methodName)) {	
					Block block = bGraph.getBlocks().get(idx);
					Iterator it = units.iterator(block.getHead(), block.getTail());
					while(it.hasNext()) {
						Unit u = (Unit)it.next();
						LineNumberTag lng;
//						while ((lng = (LineNumberTag) u.getTag("LineNumberTag")) == null) {
//							u = units.getSuccOf(u);
//						}
						if((lng = (LineNumberTag) u.getTag("LineNumberTag")) == null)
							continue;
						
						int ln = lng.getLineNumber();
						stmtStatistic.setStmt(className, ln, blkStatistic.getProperty(className, methodName, idx));
					}
				}
			}
			
		}

	}

	public void rank() {
		for(String klassName : stmtStatistic.getKlassNames()) {
			for(Integer ln : stmtStatistic.getLine(klassName)) {
				StatisticProperty sp = stmtStatistic.getProperty(klassName, ln);
				stmtResult.add(klassName, ln, sp.label);
			}
		}
		stmtResult.rank();
		Global.v().result = stmtResult;
	}

	
	
	@Override
	public void prepaerProfile(File file, List<String> args) {
		StatisticProvider p = new BlkStatisticProvider();
		try {
			blkStatistic = (BlkStatistic) p.provideData(file, args);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void toXML(File f) {
		stmtResult.toXML(f);
	}

}
